#ifndef SYNTHENICCLOUD_H
#define SYNTHENICCLOUD_H

#include "headers.h"
#include "Cluster.h"
#include "AnchorPoint.h"

class SynthenicCloud : public Cluster
{

public:

    /**
    * Default Constructor
    */
    SynthenicCloud();

    /**
     * Constructs a synthenic cloud object from a packed buffer
     * @param buffer Buffer that contains serialized cloud object
     */
    SynthenicCloud(const char *buffer);

    /**
    * Copy Constructor
    */
    SynthenicCloud(const SynthenicCloud& sCloud);

    /**
    * Assignment Operator
    */
    SynthenicCloud& operator=(const SynthenicCloud& sCloud);

    /**
    * Destructor
    */
    ~SynthenicCloud();

    //PUBLIC METHODS

    /**
    * Add AP to Synthenic Cloud
    *@param x first index of AP in GHM
    *@param y second index of AP in GHM
    */
    void addAnchorPoint ( int x, int y );
    void addAnchorPoint ( const AnchorPoint& a );

    /**
    *@Return Number of AP in cloud
    */
    uint getCountAnchorPoints() const;

    /**
    * @Return for all anchorpoints the lowest x coordinate
    */
    uint getLowestX() const;

    /**
    * @Return for all anchorpoints the highest x coordinate
    */
    uint getHighestX() const;

    /**
    * @Return for all anchorpoints the lowest y coordinate
    */
    uint getLowestY() const;

    /**
    * @Return for all anchorpoints the highest y coordinate
    */
    uint getHighestY() const;

    /**
    * @Return the probability that cloud has been generated by chance following a (complement) cumulative binomial distribution function
    * O(AP) complexity!
    */
    double calculateProbabilityBinomialD(double APDensity) const;

    /**
    * @Return the probability that cloud has been generated by chance following a self designed corrected 
    * cumulative binomial distribution function
    * This distribution excludes all configurations with tandem duplication, making the universe alot smaller
    * and prediction much more accurate
    * O(min(dimX,dimY)) complexity! (almost same as O(AP))
    */
    double calculateProbabilityBinomialDCorr(double APDensity) const;

    /**
    * @return number of AP divided by the maximum number of AP possible (maximum being defined is max one per row/column in GHM)
    */
    double calculateCloudDensity() const;

    /**
    * @return a vector with AP which are within a distance of framethickness from the bounding box edge
    */
    vector<AnchorPoint> calcAPInOuterFrame(uint frameThickness) const;

    /**
    * @return dimensions of bounding box
    */
    uint calculateBoxWidth() const {
        return end_x-begin_x+1;
    }

    uint calculateBoxHeight() const {
        return end_y-begin_y+1;
    }

    void setID(uint ID) {
        clusterID = ID;
    }

    uint getID() const {
        return clusterID;
    }

    /**
    * @return kspd from point to closest point in cloud
    */
    uint distanceToCloud(int x, int y) const;

    /**
    * @return const_iterator to begin of AP vector
    */
    vector<AnchorPoint>::const_iterator getAPBegin() const
    {
        return anchorPoints.begin();
    }

    /**
    * @return const_iterator to past the end of AP vector
    */
    vector<AnchorPoint>::const_iterator getAPEnd() const
    {
        return anchorPoints.end();
    }

    /**
    * @return true if coordinate x,y is inside the bounding box of the cloud
    */
    bool coordInCloudBox(int x, int y) const;

    /**MPI functions**/

    /**
     * Calculate and return the package size (in byte)
     */
    int getPackSize() const;

    /**
     * Pack data in a char stream
     * @param buffer Pre-allocated buffer to store the data in
     * @return The number of chars in the buffer that were used
     */
    int pack(char *buffer) const;

    /**
     * Get the number of chars to pack a number of clouds
     * @param clouds Clouds to be packed
     * @return Number of chars to pack the multiplicons
     */
    static int getPackSize(const vector<SynthenicCloud*> &clouds);

    /**
     * Pack multiplicons in a buffer
     * @param clouds Clouds to be packed
     * @param buffer Pre-allocated buffer (output)
     */
    static void packSynthenicClouds(const vector<SynthenicCloud*> &clouds,
                                 char* buffer);

    /**
     * Unpack multiplicons in a buffer
     * @param buffer Buffer that contains packed clouds
     * @param clouds clouds to be unpacked (output)
     */
    static void unpackSynthenicClouds(const char *buffer,
                                     vector<SynthenicCloud*>& clouds);


    bool probabilitySet() const{ return random_probability!=-1.0;};
    double getRandomProbability() const;

    void setRandomProbabilityBinomialD(double APDensity);
    void setRandomProbabilityBinomialDCorr(double APDensity);

private:

    vector<AnchorPoint> anchorPoints;
    uint clusterID;
    double random_probability;

    /**
    * Modify bounding box such that AP with coordinates (x,y) is in box
    */
    void modifyBoundingBox(int x,int y);

};

#endif
